Part 1: RNA

Load RNA samples

Out of 30 samples, we selected 18 for this study. These are the normal tissue samples form the control, the UVA and the UVA+SFN treatment groups. normal tissue samples from the UVB_UA groups as well as tumor samples were excluded from this analysis.
First, we removed 7,148 genes with zero counts in > 80% (> 14 out of 18) of samples. 17,273 out of 24,421 genes left.

[1] 7148
[1] 17273

Transcripts per kilobase million (TPM) normalization

Next, we noramized the counts. To convert number of hits to the relative abundane of genes in each sample, we used transcripts per kilobase million (TPM) normalization, which is as following for the j-th sample:
1. normilize for gene length: a[i, j] = 1,000*count[i, j]/gene[i, j] length(bp)
2. normalize for seq depth (i.e. total count): a(i, j)/sum(a[, j])
3. multiply by one million
A very good comparison of normalization techniques can be found at the following video:
RPKM, FPKM and TPM, clearly explained

After the normalization, each sample’s total is 1M:

02w_CON_0 02w_CON_1 02w_SFN_0 02w_SFN_1 02w_UVB_0 02w_UVB_1 15w_CON_0 15w_CON_1 15w_SFN_0 
    1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06 
15w_SFN_1 15w_UVB_0 15w_UVB_1 25w_CON_0 25w_CON_1 25w_SFN_0 25w_SFN_1 25w_UVB_0 25w_UVB_1 
    1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06 

Top 100 most abundant RNA molecules

# Separate top 100 abundant genes

tmp <- melt.data.table(data = tmp,
                       id.vars = 1:2,
                       measure.vars = 3:ncol(tmp),
                       variable.name = "Sample",
                       value.name = "TPM")

tmp$Week <- substr(x = tmp$Sample,
                   start = 1,
                   stop = 3)
tmp$Week <- factor(tmp$Week,
                   levels = unique(tmp$Week))


tmp$Treatment <- substr(x = tmp$Sample,
                        start = 5,
                        stop = 7)
tmp$Treatment <- factor(tmp$Treatment,
                        levels = c("CON", 
                                   "UVB",
                                   "SFN"))

tmp$Replica <- substr(x = tmp$Sample,
                      start = 9,
                      stop = 9)
tmp$Replica <- factor(tmp$Replica,
                      levels = 0:1)

# Plot top 100 abundant genes
p2 <- ggplot(tmp,
             aes(x = TPM,
                 y = Geneid,
                 fill = Treatment,
                 shape = Week)) +
  # facet_wrap(~ Sex, nrow = 1) +
  geom_point(size = 3,
             alpha = 0.5) +
  geom_vline(xintercept = 1,
             linetype = "dashed")
ggplotly(p2)

Bottom 100 least abundant RNA molecules

tmp <- droplevels(tpm[Geneid %in% levels(tpm$Geneid)[1:100]])

tmp <- melt.data.table(data = tmp,
                       id.vars = 1:2,
                       measure.vars = 3:ncol(tmp),
                       variable.name = "Sample",
                       value.name = "TPM")

tmp$Week <- substr(x = tmp$Sample,
                   start = 1,
                   stop = 3)
tmp$Week <- factor(tmp$Week,
                   levels = unique(tmp$Week))


tmp$Treatment <- substr(x = tmp$Sample,
                        start = 5,
                        stop = 7)
tmp$Treatment <- factor(tmp$Treatment,
                        levels = c("CON", 
                                   "UVB",
                                   "SFN"))

tmp$Replica <- substr(x = tmp$Sample,
                      start = 9,
                      stop = 9)
tmp$Replica <- factor(tmp$Replica,
                      levels = 0:1)

# Plot top 100 abundant genes
p3 <- ggplot(tmp,
             aes(x = TPM,
                 y = Geneid,
                 fill = Treatment,
                 shape = Week)) +
  # facet_wrap(~ Sex, nrow = 1) +
  geom_point(size = 3,
             alpha = 0.5) +
  geom_vline(xintercept = 1,
             linetype = "dashed")
ggplotly(p3)

Meta data

dmeta <- data.table(Sample = colnames(dt1)[-c(1:2)])

dmeta$time <- substr(x = dmeta$Sample,
                   start = 1,
                   stop = 3)
dmeta$time <- factor(dmeta$time,
                   levels = c("02w",
                              "15w",
                              "25w"))
dmeta$Week <- factor(dmeta$time,
                   levels = c("02w",
                              "15w",
                              "25w"),
                   labels = c("Week 2",
                              "Week 15",
                              "Week 25"))

dmeta$trt <- substr(x = dmeta$Sample,
                        start = 5,
                        stop = 7)
dmeta$trt <- factor(dmeta$trt,
                        levels = c("CON", 
                                   "UVB",
                                   "SFN"))
dmeta$Treatment <- factor(dmeta$trt,
                        levels = c("CON", 
                                   "UVB",
                                   "SFN"),
                        labels = c("Negative Control",
                                   "Positive Control (UVB)",
                                   "Sulforaphane (SFN)"))

dmeta$Replica <- substr(x = dmeta$Sample,
                      start = 9,
                      stop = 9)
dmeta$Replica <- factor(dmeta$Replica,
                      levels = 0:1)

datatable(dmeta,
          options = list(pageLength = nrow(dmeta)))

PCA of TPM

NOTE: the distributions are skewed. To make them symmetric, log transformation is often applied. However, there is an issue of zeros. In this instance, we added a small values lambda[i] equal to 1/10 of the smallest non-zero value of i-th gene.

dm.tpm <- as.matrix(tpm[, -c(1:2), with = FALSE])
rownames(dm.tpm) <- tpm$Geneid

# # Remove 02w_CON_1 sample and redo PCA
# dm.tpm <- dm.tpm[, colnames(dm.tpm) != "02w_CON_1"]
# dmeta <- dmeta[dmeta$Sample != "02w_CON_1", ]

# Add lambdas to all values, then take a log
dm.ltpm <- t(apply(X = dm.tpm,
                      MARGIN = 1,
                      FUN = function(a) {
                        lambda <- min(a[a > 0])/10
                        log(a + lambda)
                      }))

# PCA----
m1 <- prcomp(t(dm.ltpm),
             center = TRUE,
             scale. = TRUE)

s1 <- summary(m1)
s1
Importance of components:
                           PC1     PC2     PC3      PC4      PC5      PC6      PC7      PC8
Standard deviation     70.7928 56.9107 50.8898 28.84564 26.51968 24.81005 23.85276 22.63644
Proportion of Variance  0.2901  0.1875  0.1499  0.04817  0.04072  0.03564  0.03294  0.02967
Cumulative Proportion   0.2901  0.4777  0.6276  0.67575  0.71647  0.75211  0.78504  0.81471
                            PC9     PC10     PC11     PC12     PC13     PC14     PC15     PC16
Standard deviation     20.97344 20.20442 19.24099 19.01279 18.73783 18.53642 17.87923 17.65132
Proportion of Variance  0.02547  0.02363  0.02143  0.02093  0.02033  0.01989  0.01851  0.01804
Cumulative Proportion   0.84018  0.86381  0.88524  0.90617  0.92650  0.94639  0.96490  0.98293
                           PC17      PC18
Standard deviation     17.16891 2.134e-13
Proportion of Variance  0.01707 0.000e+00
Cumulative Proportion   1.00000 1.000e+00

Pareto chart of variance explained by principal components

imp <- data.table(PC = colnames(s1$importance),
                  Variance = 100*s1$importance[2, ],
                  Cumulative = 100*s1$importance[3, ])
imp$PC <- factor(imp$PC,
                 levels = imp$PC)
p1 <- ggplot(imp,
             aes(x = PC,
                 y = Variance)) +
  geom_bar(stat = "identity",
           fill = "grey",
           color = "black") +
  geom_line(aes(y = rescale(Cumulative,
                            to = c(min(Cumulative)*30/100,
                                   30)),
                group = rep(1, nrow(imp)))) +
  geom_point(aes(y = rescale(Cumulative,
                             to = c(min(Cumulative)*30/100,
                                    30)))) +
  scale_y_continuous("% Variance Explained",
                     breaks = seq(0, 30, by = 5),
                     labels = paste(seq(0, 30, by = 5),
                                    "%",
                                    sep = ""),
                     sec.axis = sec_axis(trans = ~.,
                                         name = "% Cumulative Variance",
                                         breaks = seq(0, 30, length.out = 5),
                                         labels = paste(seq(0, 100, length.out = 5),
                                                        "%",
                                                        sep = ""))) +
  scale_x_discrete("") +
  theme(axis.text.x = element_text(angle = 90,
                                   hjust = 1))
p1

First 3 principal components, pairwise

# Biplot while keep only the most important variables (Javier)----
# Select PC-s to pliot (PC1 & PC2)
choices <- c(1:3)

# Scores, i.e. points (df.u)
dt.scr <- data.table(m1$x[, choices])
# Add grouping variables
dt.scr$trt <- dmeta$trt
dt.scr$time <- dmeta$time
dt.scr$sample <- dmeta$Sample

# Loadings, i.e. arrows (df.v)
dt.rot <- as.data.frame(m1$rotation[, choices])
dt.rot$feat <- rownames(dt.rot)
dt.rot <- data.table(dt.rot)

# Axis labels
u.axis.labs <- paste(colnames(dt.rot)[choices], 
                     sprintf('(%0.1f%% explained var.)', 
                             100*m1$sdev[choices]^2/sum(m1$sdev^2)))

p1 <- ggplot(data = dt.scr,
             aes(x = PC1,
                 y = PC2,
                 color = trt,
                 shape = time)) +
  geom_point(size = 4,
             alpha = 0.5) +
  scale_x_continuous(u.axis.labs[1]) +
  scale_y_continuous(u.axis.labs[2])
ggplotly(p1)


p1 <- ggplot(data = dt.scr,
             aes(x = PC1,
                 y = PC3,
                 color = trt,
                 shape = time)) +
  geom_point(size = 4,
             alpha = 0.5) +
  scale_x_continuous(u.axis.labs[1]) +
  scale_y_continuous(u.axis.labs[3])
ggplotly(p1)


p1 <- ggplot(data = dt.scr,
             aes(x = PC2,
                 y = PC3,
                 color = trt,
                 shape = time)) +
  geom_point(size = 4,
             alpha = 0.5) +
  scale_x_continuous(u.axis.labs[2]) +
  scale_y_continuous(u.axis.labs[3])
ggplotly(p1)

First 3 principal components, 3D

scatterplot3js(x = dt.scr$PC1, 
               y = dt.scr$PC2, 
               z = dt.scr$PC3, 
               color = as.numeric(dt.scr$trt),
               renderer = "auto",
               pch = dt.scr$sample,
               size = 0.1)

Is sample 02w_CON_1 an outlier?

tmp <- tpm[Geneid %in% levels(tpm$Geneid)[(nrow(tpm) - 8):nrow(tpm)]]
tmp <- melt.data.table(data = tmp,
                       id.vars = 1,
                       measure.vars = 3:ncol(tmp),
                       variable.name = "Sample",
                       value.name = "TPM")
tmp <- merge(dmeta,
             tmp,
             by = "Sample")

p1 <- ggplot(tmp,
             aes(x = Week,
                 y = TPM,
                 colour = Treatment,
                 shape = Replica,
                 group = Treatment)) +
  facet_wrap(~ Geneid,
             scales = "free_y") +
  geom_point(size = 5,
             position = position_dodge(0.5)) +
  ggtitle(gene)
plot(p1)

Most abundant molecules in sample 02w_CON_1 are in larger proportions relative the total of the sample (as measured by TPM) compared to all other samples. This could be because less genes were identified in that sample compared to the rest:

colSums(tpm[, -c(1, 2)] > 0)
02w_CON_0 02w_CON_1 02w_SFN_0 02w_SFN_1 02w_UVB_0 02w_UVB_1 15w_CON_0 15w_CON_1 15w_SFN_0 
    15634     15015     15431     15133     15233     15299     15495     15901     15761 
15w_SFN_1 15w_UVB_0 15w_UVB_1 25w_CON_0 25w_CON_1 25w_SFN_0 25w_SFN_1 25w_UVB_0 25w_UVB_1 
    15793     15958     15828     15543     15344     16098     15732     16264     15979 

Conclusion

Exclude sample 02w_CON_1 form the analysis.

Session Information

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252 LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] scales_1.0.0                threejs_0.3.1               igraph_1.2.4.1             
 [4] plotly_4.9.0                ggplot2_3.1.1               readxl_1.3.1               
 [7] DESeq2_1.24.0               SummarizedExperiment_1.14.0 DelayedArray_0.10.0        
[10] BiocParallel_1.17.18        matrixStats_0.54.0          Biobase_2.44.0             
[13] GenomicRanges_1.36.0        GenomeInfoDb_1.20.0         IRanges_2.18.1             
[16] S4Vectors_0.22.0            BiocGenerics_0.30.0         DT_0.6                     
[19] data.table_1.12.2           knitr_1.22                 

loaded via a namespace (and not attached):
 [1] bitops_1.0-6           bit64_0.9-7            RColorBrewer_1.1-2     httr_1.4.0            
 [5] tools_3.6.0            backports_1.1.4        R6_2.4.0               rpart_4.1-15          
 [9] Hmisc_4.2-0            DBI_1.0.0              lazyeval_0.2.2         colorspace_1.4-1      
[13] nnet_7.3-12            withr_2.1.2            tidyselect_0.2.5       gridExtra_2.3         
[17] bit_1.1-14             compiler_3.6.0         htmlTable_1.13.1       labeling_0.3          
[21] checkmate_1.9.3        genefilter_1.66.0      stringr_1.4.0          digest_0.6.19         
[25] foreign_0.8-71         rmarkdown_1.13         XVector_0.24.0         base64enc_0.1-3       
[29] pkgconfig_2.0.2        htmltools_0.3.6        htmlwidgets_1.3        rlang_0.3.4           
[33] rstudioapi_0.10        RSQLite_2.1.1          shiny_1.3.2            jsonlite_1.6          
[37] crosstalk_1.0.0        acepack_1.4.1          dplyr_0.8.1            RCurl_1.95-4.12       
[41] magrittr_1.5           GenomeInfoDbData_1.2.1 Formula_1.2-3          Matrix_1.2-17         
[45] Rcpp_1.0.1             munsell_0.5.0          yaml_2.2.0             stringi_1.4.3         
[49] zlibbioc_1.30.0        plyr_1.8.4             grid_3.6.0             blob_1.1.1            
[53] promises_1.0.1         crayon_1.3.4           lattice_0.20-38        splines_3.6.0         
[57] annotate_1.62.0        locfit_1.5-9.1         pillar_1.4.1           geneplotter_1.62.0    
[61] XML_3.98-1.19          glue_1.3.1             evaluate_0.14          latticeExtra_0.6-28   
[65] httpuv_1.5.1           cellranger_1.1.0       gtable_0.3.0           purrr_0.3.2           
[69] tidyr_0.8.3            assertthat_0.2.1       xfun_0.7               mime_0.6              
[73] xtable_1.8-4           later_0.8.0            survival_2.44-1.1      viridisLite_0.3.0     
[77] tibble_2.1.2           AnnotationDbi_1.46.0   memoise_1.1.0          cluster_2.0.9         
LS0tDQp0aXRsZTogIlNraW4gVVZCIFNLSDEgbW91c2UgbW9kZWwgdHJlYXRlZCB3aXRoIFNGTiAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCiMgUGFydCAxOiBSTkENCmBgYHtyIGhlYWRlciwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGVycm9yID0gRkFMU0UsIHdhcm5pbmcgID1GQUxTRX0NCiMgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkNCiMgICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoIkRFU2VxMiIpDQoNCnJlcXVpcmUoa25pdHIpDQpyZXF1aXJlKGRhdGEudGFibGUpDQpyZXF1aXJlKERUKQ0KcmVxdWlyZShERVNlcTIpDQpyZXF1aXJlKHJlYWR4bCkNCnJlcXVpcmUoQmlvY1BhcmFsbGVsKQ0KcmVxdWlyZShnZ3Bsb3QyKQ0KcmVxdWlyZShwbG90bHkpDQpyZXF1aXJlKHRocmVlanMpDQpyZXF1aXJlKHNjYWxlcykNCg0KIyBOT1RFOiBvbiBERVNlcTIgT3V0cHV0OiAnYmFzZU1lYW4nIGlzIHRoZSBhdmVyYWdlIG9mIHRoZSBub3JtYWxpemVkIGNvdW50IHZhbHVlcywgDQojIGRpdmlkZWQgYnkgdGhlIHNpemUgZmFjdG9ycywgdGFrZW4gb3ZlciBhbGwgc2FtcGxlcyBpbiB0aGUgREVTZXFEYXRhU2V0DQpgYGANCg0KIyMgTG9hZCBSTkEgc2FtcGxlcw0KT3V0IG9mIDMwIHNhbXBsZXMsIHdlIHNlbGVjdGVkIDE4IGZvciB0aGlzIHN0dWR5LiBUaGVzZSBhcmUgdGhlIG5vcm1hbCB0aXNzdWUgc2FtcGxlcyBmb3JtIHRoZSBjb250cm9sLCB0aGUgVVZBIGFuZCB0aGUgVVZBK1NGTiB0cmVhdG1lbnQgZ3JvdXBzLiBub3JtYWwgdGlzc3VlIHNhbXBsZXMgZnJvbSB0aGUgVVZCX1VBIGdyb3VwcyBhcyB3ZWxsIGFzIHR1bW9yIHNhbXBsZXMgd2VyZSBleGNsdWRlZCBmcm9tIHRoaXMgYW5hbHlzaXMuICAgICANCkZpcnN0LCB3ZSByZW1vdmVkIDcsMTQ4IGdlbmVzIHdpdGggemVybyBjb3VudHMgaW4gPiA4MCUgKD4gMTQgb3V0IG9mIDE4KSBvZiBzYW1wbGVzLiAxNywyNzMgb3V0IG9mIDI0LDQyMSBnZW5lcyBsZWZ0LiANCiAgICAgICAgIA0KYGBge3IgZGF0YV9ybmEsIHdhcm5pbmcgPSBGQUxTRSwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQojIExvYWQgZGF0YS0tLS0NCmR0MCA8LSBmcmVhZCgiZGF0YS9yZW55aV9kZWR1cF9ybmFzZXFfZGF0YS9mZWF0dXJlc2NvdW50c191dmItc2tpbl9kZWR1cF9yZW55aV8yLTktMjAxOC5jc3YiLA0KICAgICAgICAgICAgIHNraXAgPSAxKQ0KDQojIFJlbW92ZSB1bnVzZWQgY29sdW1ucy0tLS0NCmR0MSA8LSBkdDBbLCBjKDEsIDY6bmNvbChkdDApKSwgd2l0aCA9IEZBTFNFXQ0KDQpjbmFtZXMgPC0gY29sbmFtZXMoZHQxKVstYygxOjIpXQ0KY25hbWVzIDwtIGdzdWIoeCA9IGNuYW1lcywNCiAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiLmRlZHVwLmJhbSIsDQogICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICIiKQ0KY29sbmFtZXMoZHQxKVstYygxOjIpXSA8LSBjbmFtZXMNCg0KIyBBVFRFTlRJT04hIEluIHRoaXMgYW5hbHlzaXMsIHdlIHdpbGwgb25seSBleGFtaW5lIGNvbnRyb2xzIGFuZCBTRk4NCiMgQWxzbywgcmVtb3ZlZCBjYW5jZXIgY2VsbCBzYW1wbGVzDQp0bmFtZXMgPC0gc3Vic3RyKHggPSBjb2xuYW1lcyhkdDEpLCANCiAgICAgICAgICAgICAgICAgc3RhcnQgPSAzLA0KICAgICAgICAgICAgICAgICBzdG9wID0gMykNCg0KZ25hbWVzIDwtIHN1YnN0cih4ID0gY29sbmFtZXMoZHQxKSwgDQogICAgICAgICAgICAgICAgIHN0YXJ0ID0gNSwNCiAgICAgICAgICAgICAgICAgc3RvcCA9IDcpDQoNCmR0MSA8LSBkdDFbLCBnbmFtZXMgJWluJSBjKCJpZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidGgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNPTiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiVVZCIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJTRk4iICkgJg0KICAgICAgICAgICAgIHRuYW1lcyAhPSAidCIsDQogICAgICAgICAgIHdpdGggPSBGQUxTRV0NCiMgMTggc2FtcGxlcyBsZWZ0DQoNCiMgUmVtb3ZlIGdlbmVzIHdpdGggemVybyBjb3VudHMgaW4gPiA4MCUgKD4gMTQgb3V0IG9mIDE4KSBvZiBzYW1wbGVzDQp0bXAgPC0gZHQxWywgLWMoMToyKV0gPT0gMA0KdG1wIDwtIHJvd1N1bXModG1wKSA+IDE0DQpzdW0odG1wKQ0KDQpkdDEgPC0gZHJvcGxldmVscyhkdDFbIXRtcCwgXSkNCm5yb3coZHQxKQ0KIyAxNywyNzMgb3V0IG9mIDI0LDQyMSBnZW5lcyBsZWZ0DQoNCmRhdGF0YWJsZShoZWFkKGR0MSwgMTApLA0KICAgICAgICAgICAgICByb3duYW1lcyA9IEZBTFNFLA0KICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTApLA0KICAgICAgICAgICAgICBjYXB0aW9uID0gIlRhYmxlIDE6IGZpcnN0IDEwIHJvd3Mgb2YgdGhlIGNvdW50IHRhYmxlIikNCmBgYA0KDQojIyBUcmFuc2NyaXB0cyBwZXIga2lsb2Jhc2UgbWlsbGlvbiAoVFBNKSBub3JtYWxpemF0aW9uDQpOZXh0LCB3ZSBub3JhbWl6ZWQgdGhlIGNvdW50cy4gVG8gY29udmVydCBudW1iZXIgb2YgaGl0cyB0byAgdGhlIHJlbGF0aXZlIGFidW5kYW5lIG9mIGdlbmVzIGluIGVhY2ggc2FtcGxlLCB3ZSB1c2VkICoqKnRyYW5zY3JpcHRzIHBlciBraWxvYmFzZSBtaWxsaW9uIChUUE0pKioqIG5vcm1hbGl6YXRpb24sIHdoaWNoIGlzIGFzIGZvbGxvd2luZyBmb3IgdGhlIGotdGggc2FtcGxlOiAgICAgICANCjEuIG5vcm1pbGl6ZSBmb3IgZ2VuZSBsZW5ndGg6IGFbaSwgal0gPSAxLDAwMCpjb3VudFtpLCBqXS9nZW5lW2ksIGpdIGxlbmd0aChicCkgICAgIA0KMi4gbm9ybWFsaXplIGZvciBzZXEgZGVwdGggKGkuZS4gdG90YWwgY291bnQpOiBhKGksIGopL3N1bShhWywgal0pICAgICANCjMuIG11bHRpcGx5IGJ5IG9uZSBtaWxsaW9uICAgICANCkEgdmVyeSBnb29kIGNvbXBhcmlzb24gb2Ygbm9ybWFsaXphdGlvbiB0ZWNobmlxdWVzIGNhbiBiZSBmb3VuZCBhdCB0aGUgZm9sbG93aW5nIHZpZGVvOiAgICANCltSUEtNLCBGUEtNIGFuZCBUUE0sIGNsZWFybHkgZXhwbGFpbmVkXShodHRwczovL3d3dy5ybmEtc2VxYmxvZy5jb20vcnBrbS1mcGttLWFuZC10cG0tY2xlYXJseS1leHBsYWluZWQvKQ0KICAgICANCkFmdGVyIHRoZSBub3JtYWxpemF0aW9uLCBlYWNoIHNhbXBsZSdzIHRvdGFsIGlzIDFNOg0KICAgICANCmBgYHtyIHRwbSwgd2FybmluZyA9IEZBTFNFLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiMgTm9ybWFsaXplIGNvdW50cyB0byBUUE0NCnRtcCA8LSAxMDAwKmR0MVssIDM6bmNvbChkdDEpXS9kdDEkTGVuZ3RoDQp0cG0gPC0gZGF0YS50YWJsZShHZW5laWQgPSBkdDEkR2VuZWlkLA0KICAgICAgICAgICAgICAgICAgTGVuZ3RoID0gZHQxJExlbmd0aCwNCiAgICAgICAgICAgICAgICAgIGFwcGx5KHRtcCwNCiAgICAgICAgICAgICAgICAgICAgICAgIDIsDQogICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihhKSB7DQogICAgICAgICAgICAgICAgICAgICAgICAgIDEwXjYqKGEvc3VtKGEpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSkpDQpjb2xTdW1zKHRwbVssIC1jKDE6MildKQ0KDQpmb3JtYXRSb3VuZChkYXRhdGFibGUoaGVhZCh0cG0sIDEwKSwNCiAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCksDQogICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJUYWJsZSAyOiB0cmFuc2NyaXB0cyBwZXIga2lsb2Jhc2UgbWlsbGlvbiAoVFBNKSBub3JtYWxpemVkIGNvdW50cyIpLA0KICAgICAgICAgICAgY29sdW1ucyA9IDM6bmNvbCh0cG0pLA0KICAgICAgICAgICAgZGlnaXRzID0gMikNCg0KIyBUb3RhbCBUUE0NCnRvdGFsIDwtIHJvd1N1bXModHBtWywgMzpuY29sKHRwbSldKQ0KDQojIFNvcnQgZ2VuZXMgYnkgcmVsYXRpdmUgYWJ1bmRhbmN5DQp0cG0kR2VuZWlkIDwtIGZhY3Rvcih0cG0kR2VuZWlkICwNCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHRwbSRHZW5laWRbb3JkZXIodG90YWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY3JlYXNpbmcgPSBGQUxTRSldKQ0KYGBgDQoNCiMgVG9wIDEwMCBtb3N0IGFidW5kYW50IFJOQSBtb2xlY3VsZXMNCmBgYHtyIG1vc3RfYWJ1bmRhbnR9DQojIFNlcGFyYXRlIHRvcCAxMDAgYWJ1bmRhbnQgZ2VuZXMNCnRtcCA8LSBkcm9wbGV2ZWxzKHRwbVtHZW5laWQgJWluJSBsZXZlbHModHBtJEdlbmVpZClbKG5yb3codHBtKSAtIDk5KTpucm93KHRwbSldXSkNCg0KdG1wIDwtIG1lbHQuZGF0YS50YWJsZShkYXRhID0gdG1wLA0KICAgICAgICAgICAgICAgICAgICAgICBpZC52YXJzID0gMToyLA0KICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSAzOm5jb2wodG1wKSwNCiAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJTYW1wbGUiLA0KICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gIlRQTSIpDQoNCnRtcCRXZWVrIDwtIHN1YnN0cih4ID0gdG1wJFNhbXBsZSwNCiAgICAgICAgICAgICAgICAgICBzdGFydCA9IDEsDQogICAgICAgICAgICAgICAgICAgc3RvcCA9IDMpDQp0bXAkV2VlayA8LSBmYWN0b3IodG1wJFdlZWssDQogICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gdW5pcXVlKHRtcCRXZWVrKSkNCg0KDQp0bXAkVHJlYXRtZW50IDwtIHN1YnN0cih4ID0gdG1wJFNhbXBsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0b3AgPSA3KQ0KdG1wJFRyZWF0bWVudCA8LSBmYWN0b3IodG1wJFRyZWF0bWVudCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkNPTiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVVZCIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNGTiIpKQ0KDQp0bXAkUmVwbGljYSA8LSBzdWJzdHIoeCA9IHRtcCRTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSA5LA0KICAgICAgICAgICAgICAgICAgICAgIHN0b3AgPSA5KQ0KdG1wJFJlcGxpY2EgPC0gZmFjdG9yKHRtcCRSZXBsaWNhLA0KICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IDA6MSkNCg0KIyBQbG90IHRvcCAxMDAgYWJ1bmRhbnQgZ2VuZXMNCnAyIDwtIGdncGxvdCh0bXAsDQogICAgICAgICAgICAgYWVzKHggPSBUUE0sDQogICAgICAgICAgICAgICAgIHkgPSBHZW5laWQsDQogICAgICAgICAgICAgICAgIGZpbGwgPSBUcmVhdG1lbnQsDQogICAgICAgICAgICAgICAgIHNoYXBlID0gV2VlaykpICsNCiAgIyBmYWNldF93cmFwKH4gU2V4LCBucm93ID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzLA0KICAgICAgICAgICAgIGFscGhhID0gMC41KSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDEsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIikNCmdncGxvdGx5KHAyKQ0KYGBgDQoNCiMgQm90dG9tIDEwMCBsZWFzdCBhYnVuZGFudCBSTkEgbW9sZWN1bGVzDQpgYGB7ciBsZWFzdF9hYnVuZGFudH0NCnRtcCA8LSBkcm9wbGV2ZWxzKHRwbVtHZW5laWQgJWluJSBsZXZlbHModHBtJEdlbmVpZClbMToxMDBdXSkNCg0KdG1wIDwtIG1lbHQuZGF0YS50YWJsZShkYXRhID0gdG1wLA0KICAgICAgICAgICAgICAgICAgICAgICBpZC52YXJzID0gMToyLA0KICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSAzOm5jb2wodG1wKSwNCiAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJTYW1wbGUiLA0KICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gIlRQTSIpDQoNCnRtcCRXZWVrIDwtIHN1YnN0cih4ID0gdG1wJFNhbXBsZSwNCiAgICAgICAgICAgICAgICAgICBzdGFydCA9IDEsDQogICAgICAgICAgICAgICAgICAgc3RvcCA9IDMpDQp0bXAkV2VlayA8LSBmYWN0b3IodG1wJFdlZWssDQogICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gdW5pcXVlKHRtcCRXZWVrKSkNCg0KDQp0bXAkVHJlYXRtZW50IDwtIHN1YnN0cih4ID0gdG1wJFNhbXBsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0b3AgPSA3KQ0KdG1wJFRyZWF0bWVudCA8LSBmYWN0b3IodG1wJFRyZWF0bWVudCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkNPTiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVVZCIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNGTiIpKQ0KDQp0bXAkUmVwbGljYSA8LSBzdWJzdHIoeCA9IHRtcCRTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSA5LA0KICAgICAgICAgICAgICAgICAgICAgIHN0b3AgPSA5KQ0KdG1wJFJlcGxpY2EgPC0gZmFjdG9yKHRtcCRSZXBsaWNhLA0KICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IDA6MSkNCg0KIyBQbG90IHRvcCAxMDAgYWJ1bmRhbnQgZ2VuZXMNCnAzIDwtIGdncGxvdCh0bXAsDQogICAgICAgICAgICAgYWVzKHggPSBUUE0sDQogICAgICAgICAgICAgICAgIHkgPSBHZW5laWQsDQogICAgICAgICAgICAgICAgIGZpbGwgPSBUcmVhdG1lbnQsDQogICAgICAgICAgICAgICAgIHNoYXBlID0gV2VlaykpICsNCiAgIyBmYWNldF93cmFwKH4gU2V4LCBucm93ID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzLA0KICAgICAgICAgICAgIGFscGhhID0gMC41KSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDEsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIikNCmdncGxvdGx5KHAzKQ0KYGBgDQoNCiMgTWV0YSBkYXRhDQpgYGB7ciBtZXRhfQ0KZG1ldGEgPC0gZGF0YS50YWJsZShTYW1wbGUgPSBjb2xuYW1lcyhkdDEpWy1jKDE6MildKQ0KDQpkbWV0YSR0aW1lIDwtIHN1YnN0cih4ID0gZG1ldGEkU2FtcGxlLA0KICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gMSwNCiAgICAgICAgICAgICAgICAgICBzdG9wID0gMykNCmRtZXRhJHRpbWUgPC0gZmFjdG9yKGRtZXRhJHRpbWUsDQogICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiMDJ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxNXciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjI1dyIpKQ0KZG1ldGEkV2VlayA8LSBmYWN0b3IoZG1ldGEkdGltZSwNCiAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCIwMnciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjE1dyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMjV3IiksDQogICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiV2VlayAyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXZWVrIDE1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXZWVrIDI1IikpDQoNCmRtZXRhJHRydCA8LSBzdWJzdHIoeCA9IGRtZXRhJFNhbXBsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0b3AgPSA3KQ0KZG1ldGEkdHJ0IDwtIGZhY3RvcihkbWV0YSR0cnQsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJDT04iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVWQiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTRk4iKSkNCmRtZXRhJFRyZWF0bWVudCA8LSBmYWN0b3IoZG1ldGEkdHJ0LA0KICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiQ09OIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVVkIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0ZOIiksDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJOZWdhdGl2ZSBDb250cm9sIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBvc2l0aXZlIENvbnRyb2wgKFVWQikiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU3VsZm9yYXBoYW5lIChTRk4pIikpDQoNCmRtZXRhJFJlcGxpY2EgPC0gc3Vic3RyKHggPSBkbWV0YSRTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSA5LA0KICAgICAgICAgICAgICAgICAgICAgIHN0b3AgPSA5KQ0KZG1ldGEkUmVwbGljYSA8LSBmYWN0b3IoZG1ldGEkUmVwbGljYSwNCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSAwOjEpDQoNCmRhdGF0YWJsZShkbWV0YSwNCiAgICAgICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gbnJvdyhkbWV0YSkpKQ0KYGBgDQoNCiMgUENBIG9mIFRQTQ0KTk9URTogdGhlIGRpc3RyaWJ1dGlvbnMgYXJlIHNrZXdlZC4gVG8gbWFrZSB0aGVtIHN5bW1ldHJpYywgbG9nIHRyYW5zZm9ybWF0aW9uIGlzIG9mdGVuIGFwcGxpZWQuIEhvd2V2ZXIsIHRoZXJlIGlzIGFuIGlzc3VlIG9mIHplcm9zLiBJbiB0aGlzIGluc3RhbmNlLCB3ZSBhZGRlZCBhIHNtYWxsIHZhbHVlcyAqKipsYW1iZGFbaV0qKiogZXF1YWwgdG8gMS8xMCBvZiB0aGUgc21hbGxlc3Qgbm9uLXplcm8gdmFsdWUgb2YgKmkqLXRoIGdlbmUuIA0KYGBge3IgcGNhfQ0KZG0udHBtIDwtIGFzLm1hdHJpeCh0cG1bLCAtYygxOjIpLCB3aXRoID0gRkFMU0VdKQ0Kcm93bmFtZXMoZG0udHBtKSA8LSB0cG0kR2VuZWlkDQoNCiMgIyBSZW1vdmUgMDJ3X0NPTl8xIHNhbXBsZSBhbmQgcmVkbyBQQ0ENCiMgZG0udHBtIDwtIGRtLnRwbVssIGNvbG5hbWVzKGRtLnRwbSkgIT0gIjAyd19DT05fMSJdDQojIGRtZXRhIDwtIGRtZXRhW2RtZXRhJFNhbXBsZSAhPSAiMDJ3X0NPTl8xIiwgXQ0KDQojIEFkZCBsYW1iZGFzIHRvIGFsbCB2YWx1ZXMsIHRoZW4gdGFrZSBhIGxvZw0KZG0ubHRwbSA8LSB0KGFwcGx5KFggPSBkbS50cG0sDQogICAgICAgICAgICAgICAgICAgICAgTUFSR0lOID0gMSwNCiAgICAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbihhKSB7DQogICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPC0gbWluKGFbYSA+IDBdKS8xMA0KICAgICAgICAgICAgICAgICAgICAgICAgbG9nKGEgKyBsYW1iZGEpDQogICAgICAgICAgICAgICAgICAgICAgfSkpDQoNCiMgUENBLS0tLQ0KbTEgPC0gcHJjb21wKHQoZG0ubHRwbSksDQogICAgICAgICAgICAgY2VudGVyID0gVFJVRSwNCiAgICAgICAgICAgICBzY2FsZS4gPSBUUlVFKQ0KDQpzMSA8LSBzdW1tYXJ5KG0xKQ0KczENCmBgYA0KDQojIFBhcmV0byBjaGFydCBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgcHJpbmNpcGFsIGNvbXBvbmVudHMNCmBgYHtyIHBjYV92YXJfcGxvdH0NCmltcCA8LSBkYXRhLnRhYmxlKFBDID0gY29sbmFtZXMoczEkaW1wb3J0YW5jZSksDQogICAgICAgICAgICAgICAgICBWYXJpYW5jZSA9IDEwMCpzMSRpbXBvcnRhbmNlWzIsIF0sDQogICAgICAgICAgICAgICAgICBDdW11bGF0aXZlID0gMTAwKnMxJGltcG9ydGFuY2VbMywgXSkNCmltcCRQQyA8LSBmYWN0b3IoaW1wJFBDLA0KICAgICAgICAgICAgICAgICBsZXZlbHMgPSBpbXAkUEMpDQpwMSA8LSBnZ3Bsb3QoaW1wLA0KICAgICAgICAgICAgIGFlcyh4ID0gUEMsDQogICAgICAgICAgICAgICAgIHkgPSBWYXJpYW5jZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsDQogICAgICAgICAgIGZpbGwgPSAiZ3JleSIsDQogICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSByZXNjYWxlKEN1bXVsYXRpdmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBjKG1pbihDdW11bGF0aXZlKSozMC8xMDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMwKSksDQogICAgICAgICAgICAgICAgZ3JvdXAgPSByZXAoMSwgbnJvdyhpbXApKSkpICsNCiAgZ2VvbV9wb2ludChhZXMoeSA9IHJlc2NhbGUoQ3VtdWxhdGl2ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBjKG1pbihDdW11bGF0aXZlKSozMC8xMDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAzMCkpKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoIiUgVmFyaWFuY2UgRXhwbGFpbmVkIiwNCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAzMCwgYnkgPSA1KSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHBhc3RlKHNlcSgwLCAzMCwgYnkgPSA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSwNCiAgICAgICAgICAgICAgICAgICAgIHNlYy5heGlzID0gc2VjX2F4aXModHJhbnMgPSB+LiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIlIEN1bXVsYXRpdmUgVmFyaWFuY2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMzAsIGxlbmd0aC5vdXQgPSA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUoc2VxKDAsIDEwMCwgbGVuZ3RoLm91dCA9IDUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSkpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZSgiIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDEpKQ0KcDENCmBgYA0KDQojIEZpcnN0IDMgcHJpbmNpcGFsIGNvbXBvbmVudHMsIHBhaXJ3aXNlDQpgYGB7ciBwY2FfcGxvdHN9DQojIEJpcGxvdCB3aGlsZSBrZWVwIG9ubHkgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlcyAoSmF2aWVyKS0tLS0NCiMgU2VsZWN0IFBDLXMgdG8gcGxpb3QgKFBDMSAmIFBDMikNCmNob2ljZXMgPC0gYygxOjMpDQoNCiMgU2NvcmVzLCBpLmUuIHBvaW50cyAoZGYudSkNCmR0LnNjciA8LSBkYXRhLnRhYmxlKG0xJHhbLCBjaG9pY2VzXSkNCiMgQWRkIGdyb3VwaW5nIHZhcmlhYmxlcw0KZHQuc2NyJHRydCA8LSBkbWV0YSR0cnQNCmR0LnNjciR0aW1lIDwtIGRtZXRhJHRpbWUNCmR0LnNjciRzYW1wbGUgPC0gZG1ldGEkU2FtcGxlDQoNCiMgTG9hZGluZ3MsIGkuZS4gYXJyb3dzIChkZi52KQ0KZHQucm90IDwtIGFzLmRhdGEuZnJhbWUobTEkcm90YXRpb25bLCBjaG9pY2VzXSkNCmR0LnJvdCRmZWF0IDwtIHJvd25hbWVzKGR0LnJvdCkNCmR0LnJvdCA8LSBkYXRhLnRhYmxlKGR0LnJvdCkNCg0KIyBBeGlzIGxhYmVscw0KdS5heGlzLmxhYnMgPC0gcGFzdGUoY29sbmFtZXMoZHQucm90KVtjaG9pY2VzXSwgDQogICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKCcoJTAuMWYlJSBleHBsYWluZWQgdmFyLiknLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMTAwKm0xJHNkZXZbY2hvaWNlc11eMi9zdW0obTEkc2Rldl4yKSkpDQoNCnAxIDwtIGdncGxvdChkYXRhID0gZHQuc2NyLA0KICAgICAgICAgICAgIGFlcyh4ID0gUEMxLA0KICAgICAgICAgICAgICAgICB5ID0gUEMyLA0KICAgICAgICAgICAgICAgICBjb2xvciA9IHRydCwNCiAgICAgICAgICAgICAgICAgc2hhcGUgPSB0aW1lKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSA0LA0KICAgICAgICAgICAgIGFscGhhID0gMC41KSArDQogIHNjYWxlX3hfY29udGludW91cyh1LmF4aXMubGFic1sxXSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModS5heGlzLmxhYnNbMl0pDQpnZ3Bsb3RseShwMSkNCg0KcDEgPC0gZ2dwbG90KGRhdGEgPSBkdC5zY3IsDQogICAgICAgICAgICAgYWVzKHggPSBQQzEsDQogICAgICAgICAgICAgICAgIHkgPSBQQzMsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gdHJ0LA0KICAgICAgICAgICAgICAgICBzaGFwZSA9IHRpbWUpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDQsDQogICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKHUuYXhpcy5sYWJzWzFdKSArDQogIHNjYWxlX3lfY29udGludW91cyh1LmF4aXMubGFic1szXSkNCmdncGxvdGx5KHAxKQ0KDQpwMSA8LSBnZ3Bsb3QoZGF0YSA9IGR0LnNjciwNCiAgICAgICAgICAgICBhZXMoeCA9IFBDMiwNCiAgICAgICAgICAgICAgICAgeSA9IFBDMywNCiAgICAgICAgICAgICAgICAgY29sb3IgPSB0cnQsDQogICAgICAgICAgICAgICAgIHNoYXBlID0gdGltZSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gNCwNCiAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXModS5heGlzLmxhYnNbMl0pICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHUuYXhpcy5sYWJzWzNdKQ0KZ2dwbG90bHkocDEpDQpgYGANCg0KIyBGaXJzdCAzIHByaW5jaXBhbCBjb21wb25lbnRzLCAzRA0KYGBge3IgcGNhXzNkLCBmaWcuaGVpZ2h0ID0gMTAsIGZpZy53aWR0aCA9IDEwfQ0Kc2NhdHRlcnBsb3QzanMoeCA9IGR0LnNjciRQQzEsIA0KICAgICAgICAgICAgICAgeSA9IGR0LnNjciRQQzIsIA0KICAgICAgICAgICAgICAgeiA9IGR0LnNjciRQQzMsIA0KICAgICAgICAgICAgICAgY29sb3IgPSBhcy5udW1lcmljKGR0LnNjciR0cnQpLA0KICAgICAgICAgICAgICAgcmVuZGVyZXIgPSAiYXV0byIsDQogICAgICAgICAgICAgICBwY2ggPSBkdC5zY3Ikc2FtcGxlLA0KICAgICAgICAgICAgICAgc2l6ZSA9IDAuMSkNCmBgYA0KDQojIElzIHNhbXBsZSAqKjAyd19DT05fMSoqIGFuIG91dGxpZXI/DQpgYGB7ciBleGNsdWRlX3NhbXBsZSwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aCA9IDEwfQ0KdG1wIDwtIHRwbVtHZW5laWQgJWluJSBsZXZlbHModHBtJEdlbmVpZClbKG5yb3codHBtKSAtIDgpOm5yb3codHBtKV1dDQp0bXAgPC0gbWVsdC5kYXRhLnRhYmxlKGRhdGEgPSB0bXAsDQogICAgICAgICAgICAgICAgICAgICAgIGlkLnZhcnMgPSAxLA0KICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSAzOm5jb2wodG1wKSwNCiAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJTYW1wbGUiLA0KICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gIlRQTSIpDQp0bXAgPC0gbWVyZ2UoZG1ldGEsDQogICAgICAgICAgICAgdG1wLA0KICAgICAgICAgICAgIGJ5ID0gIlNhbXBsZSIpDQoNCnAxIDwtIGdncGxvdCh0bXAsDQogICAgICAgICAgICAgYWVzKHggPSBXZWVrLA0KICAgICAgICAgICAgICAgICB5ID0gVFBNLA0KICAgICAgICAgICAgICAgICBjb2xvdXIgPSBUcmVhdG1lbnQsDQogICAgICAgICAgICAgICAgIHNoYXBlID0gUmVwbGljYSwNCiAgICAgICAgICAgICAgICAgZ3JvdXAgPSBUcmVhdG1lbnQpKSArDQogIGZhY2V0X3dyYXAofiBHZW5laWQsDQogICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeSIpICsNCiAgZ2VvbV9wb2ludChzaXplID0gNSwNCiAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSkpICsNCiAgZ2d0aXRsZShnZW5lKQ0KcGxvdChwMSkNCmBgYA0KTW9zdCBhYnVuZGFudCBtb2xlY3VsZXMgaW4gc2FtcGxlICoqMDJ3X0NPTl8xKiogYXJlIGluIGxhcmdlciBwcm9wb3J0aW9ucyByZWxhdGl2ZSB0aGUgdG90YWwgb2YgdGhlIHNhbXBsZSAoYXMgbWVhc3VyZWQgYnkgVFBNKSBjb21wYXJlZCB0byBhbGwgb3RoZXIgc2FtcGxlcy4gVGhpcyBjb3VsZCBiZSBiZWNhdXNlIGxlc3MgZ2VuZXMgd2VyZSBpZGVudGlmaWVkIGluIHRoYXQgc2FtcGxlIGNvbXBhcmVkIHRvIHRoZSByZXN0Og0KYGBge3IgdG90YWxfZ2VuZXN9DQpjb2xTdW1zKHRwbVssIC1jKDEsIDIpXSA+IDApDQpgYGANCg0KIyBDb25jbHVzaW9uDQpFeGNsdWRlIHNhbXBsZSAqKjAyd19DT05fMSoqIGZvcm0gdGhlIGFuYWx5c2lzLg0KDQojIFNlc3Npb24gSW5mb3JtYXRpb24NCmBgYHtyIGluZm8sZXZhbD1UUlVFfQ0Kc2Vzc2lvbkluZm8oKQ0KYGBg